package com.xiu.travel.file.oss.service.impl;

import com.xiu.travel.file.oss.domain.OssCallbackParam;
import com.xiu.travel.file.oss.domain.OssCallbackResult;
import com.xiu.travel.file.oss.domain.OssPolicyResult;
import com.xiu.travel.util.PicUtils;
import com.xiu.travel.util.api.CommonResult;
import com.alibaba.fastjson2.JSON;
import com.aliyun.oss.*;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import com.aliyun.oss.model.PutObjectRequest;
import com.xiu.travel.file.oss.service.OssService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;

/**
 * @author lijie
 * @date 2023/5/12 11:52
 * @description OssServiceImpl
 * <p>
 * oss上传管理Service实现类
 */
@Service
@Slf4j
public class OssServiceImpl implements OssService
{
    private static final Logger LOGGER = LoggerFactory.getLogger(OssServiceImpl.class);
    @Value("${aliyun.oss.policy.expire}")
    private int ALIYUN_OSS_EXPIRE;
    @Value("${aliyun.oss.maxSize}")
    private int ALIYUN_OSS_MAX_SIZE;
    @Value("${aliyun.oss.callback}")
    private String ALIYUN_OSS_CALLBACK;
    @Value("${aliyun.oss.bucketName}")
    private String ALIYUN_OSS_BUCKET_NAME;
    @Value("${aliyun.oss.endpoint}")
    private String ALIYUN_OSS_ENDPOINT;
    @Value("${aliyun.oss.dir.prefix[0]}")
    private String ALIYUN_OSS_DIR_IMAGES_PREFIX;

    @Value("${aliyun.oss.dir.prefix[1]}")
    private String ALIYUN_OSS_DIR_FILE_PREFIX;

    @Value("${aliyun.accessKeyId}")
    private String ALIYUN_OSS_ACCESSKEYID;

    @Value("${aliyun.accessKeySecret}")
    private String ALIYUN_OSS_ACCESSKEYSECRET;

    @Autowired
    private OSSClient ossClient;

    @Override
    public void removeFile(String file) throws IOException
    {
        // 创建OSSClient实例。
        OSS ossClient =
                new OSSClientBuilder().build("https://oss-cn-shenzhen.aliyuncs.com", ALIYUN_OSS_ACCESSKEYID, ALIYUN_OSS_ACCESSKEYSECRET);
        // 填写Bucket名称，例如examplebucket。
        String bucketName = ALIYUN_OSS_BUCKET_NAME;
        // 使用"/"作为分隔符拆分URL字符串
        String[] parts = file.split("/");
        // 获取文件名部分
        String fileName = parts[parts.length - 1];
        // 获取不带前缀的文件名部分
        String fileNameWithoutPrefix = fileName.substring(fileName.indexOf("_") + 1);

        try {
            // 删除文件或目录。如果要删除目录，目录必须为空。
            ossClient.deleteObject(bucketName, "images/"+fileName);
            ossClient.deleteObject(bucketName, "images/"+fileNameWithoutPrefix);
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    @Override
    public CommonResult<String> upFile(MultipartFile file) throws IOException
    {
        if (Objects.requireNonNull(file.getContentType())
                .contains("image/"))
        {
            CommonResult<String> url = upPic(file);
            if (url != null)
            {
                return url;
            }
        }
        else
        {
            CommonResult<String> url = uploadFile(file);
            if (url != null)
            {
                return url;
            }
        }
        return CommonResult.failed("上传失败");
    }

    @Override
    public String upFilePic(MultipartFile file) throws IOException
    {
        if (Objects.requireNonNull(file.getContentType())
                .contains("image/"))
        {
            String url = upFacePic(file);
            if (url != null)
            {
                return url;
            }
        }
        else
        {
            return null;
        }
        return null;
    }


    @Nullable
    private CommonResult<String> uploadFile(MultipartFile file) throws IOException
    {
        String filename = file.getOriginalFilename();
        InputStream inputStream = file.getInputStream();
        String objectName = ALIYUN_OSS_DIR_FILE_PREFIX + filename;

        if (!ossClient.doesObjectExist(ALIYUN_OSS_BUCKET_NAME, objectName))
        {
            // 创建OSSClient实例。
            OSS ossClient =
                    new OSSClientBuilder().build(ALIYUN_OSS_ENDPOINT, ALIYUN_OSS_ACCESSKEYID, ALIYUN_OSS_ACCESSKEYSECRET);
            try
            {
//                // 创建PutObjectRequest对象。
                PutObjectRequest
                        putObjectRequest =
                        new PutObjectRequest(ALIYUN_OSS_BUCKET_NAME, objectName, inputStream);
//                // 如果需要上传时设置存储类型和访问权限，请参考以下示例代码。
//                // ObjectMetadata metadata = new ObjectMetadata();
//                // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
//                // metadata.setObjectAcl(CannedAccessControlList.Private);
//                // putObjectRequest.setMetadata(metadata);
//
//                // 上传文件。
                ossClient.putObject(putObjectRequest);
//
//                //得到上传之后的文件地址
//                //https://bucketname.endpoint/objectnane  https://upload-demo-liu.oss-cn-hangzhou.aliyuncs.com
                String url = "https://" + ALIYUN_OSS_BUCKET_NAME + "." + ALIYUN_OSS_ENDPOINT + "/" + objectName;
                System.out.println("上传之后的文件路径" + url);

                return CommonResult.success(url);

            }
            catch (OSSException oe)
            {
                System.out.println("Caught an OSSException, which means your request made it to OSS, "
                        + "but was rejected with an error response for some reason.");
                System.out.println("Error Message:" + oe.getErrorMessage());
                System.out.println("Error Code:" + oe.getErrorCode());
                System.out.println("Request ID:" + oe.getRequestId());
                System.out.println("Host ID:" + oe.getHostId());
            }
            catch (ClientException ce)
            {
                System.out.println("Caught an ClientException, which means the client encountered "
                        + "a serious internal problem while trying to communicate with OSS, "
                        + "such as not being able to access the network.");
                System.out.println("Error Message:" + ce.getMessage());
            }
            finally
            {
                if (ossClient != null)
                {
                    ossClient.shutdown();
                }
            }
            return CommonResult.failed("上传失败");
        }
        return CommonResult.failed("上传失败");
    }

    @Nullable
    private String upFacePic(MultipartFile file) throws FileNotFoundException
    {
        if (file.isEmpty())
        {
            return null;
        }
        String filename = file.getOriginalFilename();

        String filePath = new File("src/main/resources/upload/"
                + Objects.requireNonNull(file.getOriginalFilename())
                .substring(file.getOriginalFilename().lastIndexOf(".") + 1)).getAbsolutePath();

        String fileSuffix = Objects.requireNonNull(file.getOriginalFilename())
                .substring(file.getOriginalFilename().lastIndexOf("."));

        String srcFilePath = "";

        File fileUpload = new File(filePath);
        if (!fileUpload.exists())
        {
            System.out.println(fileUpload.mkdirs());
        }
        if (!Objects.requireNonNull(file.getContentType()).contains("image/heic"))
        {
            long timeCode = System.currentTimeMillis();
            fileUpload = new File(filePath, timeCode + fileSuffix);
            srcFilePath = fileUpload.getAbsolutePath();
            String objectName = ALIYUN_OSS_DIR_IMAGES_PREFIX + timeCode + fileSuffix;
            if (fileUpload.exists())
            {
                return null;
            }
            try
            {
                file.transferTo(fileUpload);
            }
            catch (IOException e)
            {
                return null;
            }
            //图片文件压缩
            String resPath = "";
            resPath = ALIYUN_OSS_ENDPOINT + "/" + ALIYUN_OSS_DIR_IMAGES_PREFIX + "m_" + timeCode + fileSuffix;
            String picRes =
                    PicUtils.compressPicForScale(srcFilePath, filePath +
                            "/" +
                            "m_" +
                            timeCode +
                            fileSuffix, 15, 0.66, 4000, 4000);
            log.info("picRes:{}", picRes);
            File resFile = new File(picRes);
            InputStream inputStream = new FileInputStream(resFile);

            String mObjectName =
                    ALIYUN_OSS_DIR_IMAGES_PREFIX +
                            resFile.getName();

            // 创建OSSClient实例。
            OSS ossClient =
                    new OSSClientBuilder().build(ALIYUN_OSS_ENDPOINT, ALIYUN_OSS_ACCESSKEYID, ALIYUN_OSS_ACCESSKEYSECRET);

            try
            {
                // 创建PutObjectRequest对象。
                PutObjectRequest
                        putObjectRequest =
                        new PutObjectRequest(ALIYUN_OSS_BUCKET_NAME, mObjectName, inputStream);
                // 如果需要上传时设置存储类型和访问权限，请参考以下示例代码。
                // ObjectMetadata metadata = new ObjectMetadata();
                // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
                // metadata.setObjectAcl(CannedAccessControlList.Private);
                // putObjectRequest.setMetadata(metadata);

                // 上传文件。
                ossClient.putObject(putObjectRequest);
                putObjectRequest =
                        new PutObjectRequest(ALIYUN_OSS_BUCKET_NAME, objectName, new FileInputStream(fileUpload));
                ossClient.putObject(putObjectRequest);
                //得到上传之后的文件地址
                //https://bucketname.endpoint/objectnane  https://upload-demo-liu.oss-cn-hangzhou.aliyuncs.com
                String url = "https://" + ALIYUN_OSS_BUCKET_NAME + "." + ALIYUN_OSS_ENDPOINT + "/" + mObjectName;
                System.out.println("上传之后的文件路径" + url);

                return url;
            }
            catch (OSSException oe)
            {
                System.out.println("Caught an OSSException, which means your request made it to OSS, "
                        + "but was rejected with an error response for some reason.");
                System.out.println("Error Message:" + oe.getErrorMessage());
                System.out.println("Error Code:" + oe.getErrorCode());
                System.out.println("Request ID:" + oe.getRequestId());
                System.out.println("Host ID:" + oe.getHostId());
            }
            catch (ClientException ce)
            {
                System.out.println("Caught an ClientException, which means the client encountered "
                        + "a serious internal problem while trying to communicate with OSS, "
                        + "such as not being able to access the network.");
                System.out.println("Error Message:" + ce.getMessage());
            }
            finally
            {
                if (ossClient != null)
                {
                    ossClient.shutdown();
                }
            }
            return null;
        }
        return null;
    }

    @Nullable
    private CommonResult<String> upPic(MultipartFile file) throws FileNotFoundException
    {
        if (file.isEmpty())
        {
            return CommonResult.failed("未选择需上传的文件");
        }
        String filename = file.getOriginalFilename();

        String filePath = new File("src/main/resources/upload/"
                + Objects.requireNonNull(file.getOriginalFilename())
                .substring(file.getOriginalFilename().lastIndexOf(".") + 1)).getAbsolutePath();

        String fileSuffix = Objects.requireNonNull(file.getOriginalFilename())
                .substring(file.getOriginalFilename().lastIndexOf("."));

        String srcFilePath = "";

        File fileUpload = new File(filePath);
        if (!fileUpload.exists())
        {
            System.out.println(fileUpload.mkdirs());
        }
        if (!Objects.requireNonNull(file.getContentType()).contains("image/heic"))
        {
            long timeCode = System.currentTimeMillis();
            fileUpload = new File(filePath, timeCode + fileSuffix);
            srcFilePath = fileUpload.getAbsolutePath();
            String objectName = ALIYUN_OSS_DIR_IMAGES_PREFIX + timeCode + fileSuffix;
            if (fileUpload.exists())
            {
                return CommonResult.failed("上传的文件已存在");
            }
            try
            {
                file.transferTo(fileUpload);
            }
            catch (IOException e)
            {
                CommonResult.failed("上传文件到服务器失败");
            }
            //图片文件压缩
            String resPath = "";
            resPath = ALIYUN_OSS_ENDPOINT + "/" + ALIYUN_OSS_DIR_IMAGES_PREFIX + "m_" + timeCode + fileSuffix;
            String picRes =
                    PicUtils.compressPicForScale(srcFilePath, filePath +
                            "/" +
                            "m_" +
                            timeCode +
                            fileSuffix, 15, 0.66, 4000, 4000);
            log.info("picRes:{}", picRes);
            File resFile = new File(picRes);
            InputStream inputStream = new FileInputStream(resFile);

            String mObjectName =
                    ALIYUN_OSS_DIR_IMAGES_PREFIX +
                            resFile.getName();

            // 创建OSSClient实例。
            OSS ossClient =
                    new OSSClientBuilder().build(ALIYUN_OSS_ENDPOINT, ALIYUN_OSS_ACCESSKEYID, ALIYUN_OSS_ACCESSKEYSECRET);

            try
            {
                // 创建PutObjectRequest对象。
                PutObjectRequest
                        putObjectRequest =
                        new PutObjectRequest(ALIYUN_OSS_BUCKET_NAME, mObjectName, inputStream);
                // 如果需要上传时设置存储类型和访问权限，请参考以下示例代码。
                // ObjectMetadata metadata = new ObjectMetadata();
                // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
                // metadata.setObjectAcl(CannedAccessControlList.Private);
                // putObjectRequest.setMetadata(metadata);

                // 上传文件。
                ossClient.putObject(putObjectRequest);
                putObjectRequest =
                        new PutObjectRequest(ALIYUN_OSS_BUCKET_NAME, objectName, new FileInputStream(fileUpload));
                ossClient.putObject(putObjectRequest);
                //得到上传之后的文件地址
                //https://bucketname.endpoint/objectnane  https://upload-demo-liu.oss-cn-hangzhou.aliyuncs.com
                String url = "https://" + ALIYUN_OSS_BUCKET_NAME + "." + ALIYUN_OSS_ENDPOINT + "/" + mObjectName;
                System.out.println("上传之后的文件路径" + url);

                return CommonResult.success(url);
            }
            catch (OSSException oe)
            {
                System.out.println("Caught an OSSException, which means your request made it to OSS, "
                        + "but was rejected with an error response for some reason.");
                System.out.println("Error Message:" + oe.getErrorMessage());
                System.out.println("Error Code:" + oe.getErrorCode());
                System.out.println("Request ID:" + oe.getRequestId());
                System.out.println("Host ID:" + oe.getHostId());
            }
            catch (ClientException ce)
            {
                System.out.println("Caught an ClientException, which means the client encountered "
                        + "a serious internal problem while trying to communicate with OSS, "
                        + "such as not being able to access the network.");
                System.out.println("Error Message:" + ce.getMessage());
            }
            finally
            {
                if (ossClient != null)
                {
                    ossClient.shutdown();
                }
            }
            return CommonResult.failed("上传失败");
        }
        return CommonResult.failed("上传失败");
    }

    /**
     * 签名生成
     */
    @Override
    public OssPolicyResult policy()
    {
        OssPolicyResult result = new OssPolicyResult();
        // 存储目录
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        String dir = ALIYUN_OSS_DIR_IMAGES_PREFIX + sdf.format(new Date());
        // 签名有效期
        long expireEndTime = System.currentTimeMillis() + ALIYUN_OSS_EXPIRE * 1000;
        Date expiration = new Date(expireEndTime);
        // 文件大小
        long maxSize = ALIYUN_OSS_MAX_SIZE * 1024 * 1024;
        // 回调
        OssCallbackParam callback = new OssCallbackParam();
        callback.setCallbackUrl(ALIYUN_OSS_CALLBACK);
        callback.setCallbackBody("filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}");
        callback.setCallbackBodyType("application/x-www-form-urlencoded");
        // 提交节点
        String action = "http://" + ALIYUN_OSS_BUCKET_NAME + "." + ALIYUN_OSS_ENDPOINT;
        try
        {
            PolicyConditions policyConds = new PolicyConditions();
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize);
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
            String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
            String policy = BinaryUtil.toBase64String(binaryData);
            String signature = ossClient.calculatePostSignature(postPolicy);
            String callbackData = BinaryUtil.toBase64String(JSON.parse(String.valueOf(callback)).toString().getBytes(StandardCharsets.UTF_8));
            // 返回结果
            result.setAccessKeyId(ossClient.getCredentialsProvider().getCredentials().getAccessKeyId());
            result.setPolicy(policy);
            result.setSignature(signature);
            result.setDir(dir);
            result.setCallback(callbackData);
            result.setHost(action);
        }
        catch (Exception e)
        {
            LOGGER.error("签名生成失败", e);
        }
        return result;
    }

    @Override
    public OssCallbackResult callback(HttpServletRequest request)
    {
        OssCallbackResult result = new OssCallbackResult();
        String filename = request.getParameter("filename");
        filename =
                "http://".concat(ALIYUN_OSS_BUCKET_NAME).concat(".").concat(ALIYUN_OSS_ENDPOINT).concat("/").concat(filename);
        result.setFilename(filename);
        result.setSize(request.getParameter("size"));
        result.setMimeType(request.getParameter("mimeType"));
        result.setWidth(request.getParameter("width"));
        result.setHeight(request.getParameter("height"));
        return result;
    }

}
